package com.stgy.lease.web.admin.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.stgy.lease.common.exception.StgyException;
import com.stgy.lease.common.result.ResultCodeEnum;
import com.stgy.lease.model.base.BaseDomain;
import com.stgy.lease.model.domain.*;
import com.stgy.lease.web.admin.dto.RoomInfoSearchDto;
import com.stgy.lease.web.admin.dto.RoomInfoSubmitDto;
import com.stgy.lease.web.admin.mapper.RoomAttrValueMapper;
import com.stgy.lease.web.admin.service.*;
import com.stgy.lease.web.admin.mapper.RoomInfoMapper;
import com.stgy.lease.web.admin.vo.RoomDetailVo;
import com.stgy.lease.web.admin.vo.RoomItemVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author lingx
 * @description 针对表【room_info(房间信息表)】的数据库操作Service实现
 * @createDate 2024-08-28 11:00:29
 */
@Service
@Slf4j
public class RoomInfoServiceImpl extends ServiceImpl<RoomInfoMapper, RoomInfo>
        implements RoomInfoService {

    //当业务类中 A方法调用了B方法时，默认会隐式使用 this.B()
    //该场景会导致 调用B方法的动态代理失效(面试题)
    //解决：通过自动装配从容器中获取
    @Autowired
    private RoomAttrValueService roomAttrValueService;
    @Autowired
    private RoomFacilityService roomFacilityService;
    @Autowired
    private RoomLabelService roomLabelService;
    @Autowired
    private RoomPaymentTypeService roomPaymentTypeService;
    @Autowired
    private RoomLeaseTermService roomLeaseTermService;
    @Autowired
    private GraphInfoService graphInfoService;
    @Autowired
    private FileService fileService;

    //新增或更新房间信息
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = "stgy:admin:room", key = "#roomInfoSubmitDto.id")
    @Override
    public boolean saveOrUpdateRoomInfoSubmitDto(RoomInfoSubmitDto roomInfoSubmitDto) {
        //1、数据唯一性校验：同一个公寓下同一个房间必须唯一
        if (roomInfoSubmitDto == null) {
            throw new StgyException(ResultCodeEnum.DATA_ERROR);
        }
        //查询是否存在重复的房间信息
        LambdaQueryWrapper<RoomInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoomInfo::getApartmentId, roomInfoSubmitDto.getApartmentId());
        wrapper.eq(RoomInfo::getRoomNumber, roomInfoSubmitDto.getRoomNumber());
        wrapper.last("limit 1");
        RoomInfo one = this.getOne(wrapper);
        Long id = roomInfoSubmitDto.getId();
        if (id != null && !id.equals(one.getId())) {
            throw new StgyException(ResultCodeEnum.DATA_ERROR);
        }

        List<GraphInfo> graphInfos = graphInfoService.list(Wrappers.lambdaQuery(GraphInfo.class)
                .eq(GraphInfo::getItemType, 2)
                .eq(GraphInfo::getItemId, id));
        //2、判断是更新还是新增
        if (id != null) {
            //更新：先删除之前的映射缓存，保存新的映射缓存
            this.deleteRoomExtra(id);
        }

        //3 新增房间 或 更新房间
        this.saveOrUpdate(roomInfoSubmitDto);
        //保存房间的映射数据
        //获取新增后的id
        id = roomInfoSubmitDto.getId();
        //room_attr_value表
        Long finalId = id;
        if (!CollectionUtils.isEmpty(roomInfoSubmitDto.getAttrValueIds())) {
            List<RoomAttrValue> roomAttrValues = roomInfoSubmitDto.getAttrValueIds().stream()
                    .map(roomAttrValueId -> {
                        RoomAttrValue roomAttrValue = new RoomAttrValue();
                        roomAttrValue.setAttrValueId(roomAttrValueId);
                        roomAttrValue.setRoomId(finalId);
                        return roomAttrValue;
                    }).collect(Collectors.toList());
            roomAttrValueService.saveOrUpdateBatch(roomAttrValues);
        }
        //room_facility表
        if (!CollectionUtils.isEmpty(roomInfoSubmitDto.getFacilityInfoIds())) {
            List<RoomFacility> roomFacilities = roomInfoSubmitDto.getFacilityInfoIds().stream()
                    .map(roomFacilityId -> {
                        RoomFacility roomFacility = new RoomFacility();
                        roomFacility.setFacilityId(roomFacilityId);
                        roomFacility.setRoomId(finalId);
                        return roomFacility;
                    }).collect(Collectors.toList());
            roomFacilityService.saveOrUpdateBatch(roomFacilities);
        }
        //room_label表
        if (!CollectionUtils.isEmpty(roomInfoSubmitDto.getLabelInfoIds())) {
            List<RoomLabel> roomLabels = roomInfoSubmitDto.getLabelInfoIds().stream()
                    .map(roomLabelId -> {
                        RoomLabel roomLabel = new RoomLabel();
                        roomLabel.setLabelId(roomLabelId);
                        roomLabel.setRoomId(finalId);
                        return roomLabel;
                    }).collect(Collectors.toList());
            roomLabelService.saveOrUpdateBatch(roomLabels);
        }
        //room_payment_type表
        if (!CollectionUtils.isEmpty(roomInfoSubmitDto.getPaymentTypeIds())) {
            List<RoomPaymentType> roomPaymentTypes = roomInfoSubmitDto.getPaymentTypeIds().stream()
                    .map(roomPaymentTypeId -> {
                        RoomPaymentType roomPaymentType = new RoomPaymentType();
                        roomPaymentType.setPaymentTypeId(roomPaymentTypeId);
                        roomPaymentType.setRoomId(finalId);
                        return roomPaymentType;
                    }).collect(Collectors.toList());
            roomPaymentTypeService.saveOrUpdateBatch(roomPaymentTypes);
        }
        //room_lease_term表 list
        if (!CollectionUtils.isEmpty(roomInfoSubmitDto.getLeaseTermIds())) {
            List<RoomLeaseTerm> roomLeaseTerms = roomInfoSubmitDto.getLeaseTermIds().stream()
                    .map(roomLeaseTermId -> {
                        RoomLeaseTerm roomLeaseTerm = new RoomLeaseTerm();
                        roomLeaseTerm.setLeaseTermId(roomLeaseTermId);
                        roomLeaseTerm.setRoomId(finalId);
                        return roomLeaseTerm;
                    }).collect(Collectors.toList());
            roomLeaseTermService.saveOrUpdateBatch(roomLeaseTerms);
        }
        //graph_info表
        if (!CollectionUtils.isEmpty(roomInfoSubmitDto.getGraphVoList())) {
            for (GraphInfo graphInfo : roomInfoSubmitDto.getGraphVoList()) {
                GraphInfo info = new GraphInfo();
                BeanUtils.copyProperties(graphInfo, info);
                info.setId(null);
                info.setItemType(2);
                info.setItemId(finalId);
                graphInfoService.saveOrUpdate(info);
            }
        }

        //在整个业务执行完结束：发起图片删除请求 避免业务失败回滚时图片被删除了
        //文件删除：删除minio的冗余数据 减少磁盘压力
        //批量删除房间的图片列表：1、删除图片的异常被处理了，不会影响主业务 2、和主业务并行执行(子线程异步执行)
        //整合spring-task:spring-context包提供了异步的框架，只要通过简单的注解就能实现异步操作
        //1、启动类启用异步编程 2、需要异步的方法上使用异步注解
        if (!CollectionUtils.isEmpty(graphInfos)) { //新增不删除，更新才删除
            fileService.batchDelete(graphInfos, id);
        }
//        for (GraphInfo graphInfo : graphInfos) {
//            try {
//                fileService.delete(graphInfo.getUrl());
//            } catch (Exception e) {
//                log.error("stgy-roominfo-图片删除失败：url:{},roomId:{}",
//                        graphInfo.getUrl(), id);
//            }
//        }
        return true;
    }

    //条件查询房间分页列表
    @Override
    public Page<RoomItemVo> pageItem(RoomInfoSearchDto roomInfoSearchDto) {
        if (roomInfoSearchDto == null) {
            throw new StgyException(ResultCodeEnum.DATA_ERROR);
        }
        //创建分页对象：封装分页条件
        Page<RoomItemVo> page =
                new Page<>(roomInfoSearchDto.getCurrent(), roomInfoSearchDto.getSize());
        //自定义sql：使用page对象携带分页条件，使用queryWrapper携带过滤条件
        QueryWrapper<RoomItemVo> queryWrapper = Wrappers.query();
        Long provinceId = roomInfoSearchDto.getProvinceId();
        Long cityId = roomInfoSearchDto.getCityId();
        Long districtId = roomInfoSearchDto.getDistrictId();
        Long apartmentId = roomInfoSearchDto.getApartmentId();
        if (provinceId != null) {
            //生成的条件sql为：t3.province_id = 1
            queryWrapper.eq("t3.province_id", provinceId);
        }
        if (cityId != null) {
            queryWrapper.eq("t3.city_id", cityId);
        }
        if (districtId != null) {
            queryWrapper.eq("t3.district_id", districtId);
        }
        if (apartmentId != null) {
            queryWrapper.eq("t1.apartment_id", apartmentId);
        }
        queryWrapper.eq("t1.is_deleted", 0);
        List<RoomItemVo> roomItemVos = baseMapper.selectPageItem(page, queryWrapper);
        page.setRecords(roomItemVos);
        return page;
    }

    @Override
    public boolean deleteRoomExtra(Long id) {
        //删除房间映射数据
        //room_attr_value表
        roomAttrValueService.remove(new LambdaQueryWrapper<RoomAttrValue>()
                .eq(RoomAttrValue::getRoomId, id));

        //room_facility表
        roomFacilityService.remove(new LambdaQueryWrapper<RoomFacility>()
                .eq(RoomFacility::getRoomId, id));
        //room_label表
        roomLabelService.remove(new LambdaQueryWrapper<RoomLabel>()
                .eq(RoomLabel::getRoomId, id));
        //room_payment_type表
        roomPaymentTypeService.remove(new LambdaQueryWrapper<RoomPaymentType>()
                .eq(RoomPaymentType::getRoomId, id));
        //room_lease_term表 list
        roomLeaseTermService.remove(new LambdaQueryWrapper<RoomLeaseTerm>()
                .eq(RoomLeaseTerm::getRoomId, id));
        //graph_info表
        //在删除前获取房间列表
        List<GraphInfo> graphInfos = graphInfoService.list(Wrappers.lambdaQuery(GraphInfo.class)
                .eq(GraphInfo::getItemType, 2)
                .eq(GraphInfo::getItemId, id));

//            graphInfoService.remove(new LambdaQueryWrapper<GraphInfo>()
//                    .eq(GraphInfo::getItemType, 2)
//                    .eq(GraphInfo::getItemId, id));
        graphInfoService.removeBatchByIds(graphInfos.stream()
                .map(BaseDomain::getId).collect(Collectors.toList()));
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = "stgy:admin:room", key = "#id")
    @Override
    public boolean removeRoomInfoById(Long id) {
        if (id == null || id < 0) {
            throw new StgyException(ResultCodeEnum.DATA_ERROR);
        }
        this.removeById(id);
        this.deleteRoomExtra(id);
        return true;
    }

    //根据id修改房间状态
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = "stgy:admin:room", key = "#id")
    @Override
    public boolean updateReleaseStatusById(Long id, Integer status) {
        if (id == null || id < 0 || status < 0) {
            throw new StgyException(ResultCodeEnum.DATA_ERROR);
        }
        LambdaQueryWrapper<RoomInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoomInfo::getId, id);
        wrapper.last("limit 1");
        RoomInfo one = this.getOne(wrapper);
        one.setIsRelease(status);
        return this.saveOrUpdate(one);
    }

    //根据id获取房间详细信息
    //stgy:admin:room:id
    //index(固定资产)和key(支持springEL表达式 动态获取参数)用来组合 缓存的键
//    @Cacheable(value = "stgy:admin:room",key = "'xx'")
    @Cacheable(value = "stgy:admin:room", key = "#id")
    @Override
    public RoomDetailVo getDetailById(Long id) {
        /*
            如果业务是用户的业务：一般分步查询
                可以结合异步编排CompletableFuture,可以将多个数据库的操作编排为串行+并行的方式执行
                创建全局变量：RoomDetailVo
                子线程1、查询room详情 将room信息设置给RoomDetailVo
                子线程2、查询房间的标签列表 将标签列表 信息设置给RoomDetailVo
                子线程3、查询房间的配套列表 ...
                子线程4、查询房间的图片列表
                一个大sql查询：多表联查的sql执行时，之间的联查效率低
                ...
                如果一个业务方法会在多个地方使用 建议抽取业务方法
                spring-context:
                    1、异步任务
                    启动类：@EnableAsync 开启支持异步任务
                    在需要异步执行的业务方法上：@Async 使用异步任务
                    2、自动缓存管理：
                        2.1配置类上：@EnableCaching 开启自动缓存管理
                        2.2在需要缓存的业务方法(查询方法)上：@Cacheable注解 自动缓存管理
                            -方法被调用时 先检查该方法是否有缓存，如果有直接返回
                            - 如果没有，调用方法，并将执行的结果缓存
                            - 返回结果
                            @CachingPut: 新增或更新时，将方法的返回结果缓存
                            @CachingEvict: 新增或更新或删除时，将指定的缓存删除
                        2.3为springcache提供缓存管理器 CacheManage
                            spring提供了缓存管理的接口：约束API
                            -其他的缓存如果希望支持springcache，可以实现缓存管理器的接口
                            -将缓存管理器对象注入到容器中以后，自动缓存管理可以调用它对应的方法管理缓存
                          1、使用redis管理缓存
                            引入redis缓存启动器
                            redis配置
                          2、将springdataredis启动器实现了CacheManage的类的对象创建好注入到容器中
         */
        if (id == null || id < 0) {
            throw new StgyException(ResultCodeEnum.DATA_ERROR);
        }
        RoomDetailVo roomDetailVo = baseMapper.selectDetailById(id);
        return roomDetailVo;
    }

    //根据公寓id查询房间列表 两张表room_info lease_agreement status not in (1,2,5,7)
    @Override
    public List<RoomInfo> listBasicByApartmentId(Long id) {
        if (id == null || id < 0) {
            throw new StgyException(ResultCodeEnum.DATA_ERROR);
        }
        LambdaQueryWrapper<RoomInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RoomInfo::getApartmentId, id);
        wrapper.eq(RoomInfo::getIsRelease, 1);
        return this.list(wrapper);
    }
}